package com.suray.wcs.service.thread.process;

import com.suray.basic.wcs.plc.core.Plc;
import com.suray.basic.wcs.plc.enums.ConveryTaskType;
import com.suray.basic.wcs.plc.enums.Direction;
import com.suray.basic.wcs.plc.enums.PlcDeviceType;
import com.suray.basic.wcs.plc.enums.SingleConveryRunType;
import com.suray.basic.wcs.plc.pojo.oper.single.AutoTraversingOper;
import com.suray.basic.wcs.plc.pojo.oper.single.DeviceInterface;
import com.suray.basic.wcs.plc.pojo.oper.single.MonomerConveyorOper;
import com.suray.basic.wcs.utils.LoggerUtil;
import com.suray.basic.wcs.utils.WcsFuncUtil;
import com.suray.commin.core.utils.SpringContextHolder;
import com.suray.wcs.service.system.entity.TaskDB;
import com.suray.wcs.service.system.service.impl.NodeDBServiceImpl;
import com.suray.wcs.service.thread.process.util.AutoTraversProcessStatus;
import com.suray.wcs.service.util.WcsUtil;

import java.io.IOException;

public class AutoTraversProcess {
    private NodeDBServiceImpl nodeDBService = SpringContextHolder.getBean(NodeDBServiceImpl.class);

    /**
     * 当前进程状态
     */
    private AutoTraversProcessStatus processStatus;

    /**
     * 起点设备
     */
    private DeviceInterface startDevice;

    /**
     * 终点设备
     */
    private DeviceInterface endDevice;

    /**
     * 自动横移机
     */
    private AutoTraversingOper autoTraversingOper;

    private MonomerConveyorOper monomerConveyorOper;

    /**
     * 基础设备输送货物到自动横移机
     */
    private boolean startFromDevice;

    /**
     * 入库任务
     */
    private boolean taskIn;

    /**
     * 设备转动方向
     */
    private Direction direction;

    private TaskDB taskDB;

    /**
     * 流程是否结束
     */
    private boolean endProcess;

    /**
     * 流程结束后是否需要关门
     */
    private boolean needCloseDoor;

    /**
     * 流程开始前是否需要开门
     */
    private boolean needOpenDoor;

    private int timeOut = 60;

    /**
     * 用来单独控制自动横移机
     * @param autoTraversingOper
     * @param isEndDevice : 自动横移机是否为终点设备
     * @param taskIn
     */
    public AutoTraversProcess(AutoTraversingOper autoTraversingOper, boolean isEndDevice, boolean taskIn) {
        if (autoTraversingOper == null) {
            throw new RuntimeException("未获取到自动横移机对象");
        }

        if (taskIn) {
            direction = Direction.IN;
        } else {
            direction = Direction.OUT;
        }

        if (isEndDevice) {
            startFromDevice = true;
        } else {
            startFromDevice = false;
        }

        this.autoTraversingOper = autoTraversingOper;
        this.taskIn = taskIn;
        this.endProcess = false;
        this.processStatus = AutoTraversProcessStatus.INIT;
    }

    /**
     * 横移机流程
     * @param startDevice
     * @param endDevice
     * @param taskIn
     * @param taskDB
     * @param needCloseDoor
     * @param needOpenDoor
     */
    public AutoTraversProcess(DeviceInterface startDevice, DeviceInterface endDevice, boolean taskIn, TaskDB taskDB,
                              boolean needCloseDoor, boolean needOpenDoor) {
        if (startDevice == null) {
            throw new RuntimeException("未获取到起点设备");
        }

        if (endDevice == null) {
            throw new RuntimeException("未获取到终点设备");
        }

        if (startDevice.getPlcDeviceType() == PlcDeviceType.AUTO_TRAVERSING) {
            startFromDevice = false;
            autoTraversingOper = (AutoTraversingOper) startDevice;
            monomerConveyorOper = (MonomerConveyorOper) endDevice;
        } else if (endDevice.getPlcDeviceType() == PlcDeviceType.AUTO_TRAVERSING) {
            startFromDevice = true;
            autoTraversingOper = (AutoTraversingOper) endDevice;
            monomerConveyorOper = (MonomerConveyorOper) startDevice;
        } else {
            throw new RuntimeException("两个工位都没有横移机，不能执行横移机流程");
        }

        if (taskIn) {
            direction = Direction.IN;
        } else {
            direction = Direction.OUT;
        }

        this.startDevice = startDevice;
        this.endDevice = endDevice;
        this.taskIn = taskIn;
        this.taskDB = taskDB;
        this.endProcess = false;
        this.needCloseDoor = needCloseDoor;
        this.needOpenDoor = needOpenDoor;
        this.processStatus = AutoTraversProcessStatus.INIT;
        LoggerUtil.info("开门？" + needOpenDoor + ", 关门?" + needCloseDoor, this.getClass());
    }

    public void setProcessStatus(AutoTraversProcessStatus processStatus) {
        this.processStatus = processStatus;
    }

    /**
     * 启动
     */
    public void start() {
        LoggerUtil.info(startDevice.getDeviceName() + "->" + endDevice.getDeviceName() + " 输送开始", this.getClass());
        process();
    }

    private void process() {
        String interruptStr = startDevice.getDeviceName() + "->" + endDevice.getDeviceName() + ":";
        while (true) {
            try {
                if (taskDB != null) {
                    Plc.checkPLCInterrupt(taskDB.getTaskId().toString(), interruptStr + processStatus + " 中断");
                }
                autoTraverProcess();

                if (endProcess) {
                    break;
                }
            } catch (IOException e) {
                WcsFuncUtil.exceptionLog(e, this.getClass());
                LoggerUtil.error(startDevice.getDeviceName() + "->" + endDevice.getDeviceName() +
                        " 通讯异常，流程进度：" + processStatus, this.getClass());
            }

            WcsFuncUtil.delay(1000);
        }
    }

    private int checkTimeOut(int timeOut) {
        if (timeOut > 0) {
            return --timeOut;
        } else {
            throw new RuntimeException(startDevice.getDeviceName() + "->" + endDevice.getDeviceName() + ":" +
                    processStatus + "时 执行超时");
        }
    }

    /**
     * 自动横移机流程
     */
    private void autoTraverProcess() throws IOException {
        switch (processStatus) {
            case INIT:
                init();
                processStatus = AutoTraversProcessStatus.WAIT_DEVICE_PERMIT_RUN;
                LoggerUtil.info("更新任务进度为:" + processStatus, this.getClass());
                timeOut = 60;
            case WAIT_DEVICE_PERMIT_RUN:
                //等待设备状态
                checkTimeOut(timeOut);
                if (!isDevicePermitRun()) {
                    break;
                }
                processStatus = AutoTraversProcessStatus.OPEN_DOOR;
                LoggerUtil.info("更新任务进度为:" + processStatus, this.getClass());
                timeOut = 60;
            case OPEN_DOOR:
                //开门
                checkTimeOut(timeOut);
                if (needOpenDoor) {
                    if (!openStartDoor()) {
                        break;
                    }
                }
                processStatus = AutoTraversProcessStatus.WAIT_OPEN_DOOR;
                LoggerUtil.info("更新任务进度为:" + processStatus + ", 发送开门?" + needOpenDoor, this.getClass());
                timeOut = 60;
            case WAIT_OPEN_DOOR:
                //等待门开启完成
                checkTimeOut(timeOut);
                if (needOpenDoor) {
                    if (!waitOpenStartDoor()) {
                        break;
                    }
                }
                processStatus = AutoTraversProcessStatus.END_DEVICE_IN_CARGO;
                LoggerUtil.info("更新任务进度为:" + processStatus + ", 开门成功?" + needOpenDoor, this.getClass());
                timeOut = 60;
            case END_DEVICE_IN_CARGO:
                //后面的设备先转动
                if (!endDeviceInCargo()) {
                    break;
                }
                processStatus = AutoTraversProcessStatus.START_DEVICE_OUT_CARGO;
                LoggerUtil.info("更新任务进度为:" + processStatus, this.getClass());
                timeOut = 60;
            case START_DEVICE_OUT_CARGO:
                //前面的设备转动
                if (!startDeviceOutCargo()) {
                    break;
                }
                processStatus = AutoTraversProcessStatus.WAIT_DEVICE_RUN_END;
                LoggerUtil.info("更新任务进度为:" + processStatus, this.getClass());
                timeOut = 60;
            case WAIT_DEVICE_RUN_END:
                //等待设备转动完成
                checkTimeOut(timeOut);
                if (!waitInCargoEnd()) {
                    break;
                }
                processStatus = AutoTraversProcessStatus.CLOSE_DOOR;
                LoggerUtil.info("更新任务进度为:" + processStatus, this.getClass());
                timeOut = 60;
            case CLOSE_DOOR:
                //关门
                if (needCloseDoor) {
                    if (!closeStartDoor()) {
                        break;
                    }
                }
                processStatus = AutoTraversProcessStatus.WAIT_CLOSE_DOOR;
                LoggerUtil.info("更新任务进度为:" + processStatus + ", 发送关门?" + needOpenDoor, this.getClass());
                timeOut = 60;
            case WAIT_CLOSE_DOOR:
                //等待门关闭完成，如果需要等待外部直接调取waitCloseStartDoor，这里默认不需要等待
                checkTimeOut(timeOut);
                if (needCloseDoor) {
                    if (!waitCloseStartDoor()) {
                        break;
                    }
                }
                processStatus = AutoTraversProcessStatus.END;
                LoggerUtil.info("更新任务进度为:" + processStatus + ", 关门成功?" + needOpenDoor, this.getClass());
                timeOut = 60;
            case END:
                end();
                break;
            default:
                throw new RuntimeException(startDevice.getDeviceName() + "->" + endDevice.getDeviceName() +
                        " 进度错误：" + processStatus);
        }
    }

    /**
     * 初始化
     */
    private void init() {
        if (!startDevice.hasCargo()) {
            throw new RuntimeException(startDevice.getDeviceName() + " 无货，无法输送货物至 " + endDevice.getDeviceName());
        }
    }

    /**
     * 等待设备允许运行
     * @return
     */
    public boolean isDevicePermitRun() {
        boolean endHasCargo = WcsUtil.isNodeHasCargo(endDevice.getCoord(), nodeDBService);
        if (startDevice.permitRun() && endDevice.permitRun() && !endDevice.hasCargo() && !endHasCargo) {
            if (needOpenDoor) {
                if (autoTraversingOper.isInMidPlace()) {
                    return true;
                }

                if (taskIn == startFromDevice) {
                    if (autoTraversingOper.isInPlaceSignal1()) {
                        LoggerUtil.info("1方向到位，无需开门", this.getClass());
                        needOpenDoor = false;
                        return true;
                    }
                } else {
                    if (autoTraversingOper.isInPlaceSignal3()) {
                        LoggerUtil.info("3方向到位，无需开门", this.getClass());
                        needOpenDoor = false;
                        return true;
                    }
                }
            } else {
                return true;
            }
        }

        LoggerUtil.info(startDevice.getDeviceName() + "允许运行？" + startDevice.permitRun() + ", " +
                        endDevice.getDeviceName() + "允许运行？" + endDevice.permitRun() +
                        ", 终点有货？" + (endDevice.hasCargo() || endHasCargo) +
                        ", " + autoTraversingOper.getDeviceName() +
                        ", 在原点，需在原点？" + autoTraversingOper.isInMidPlace() + ", " + !needOpenDoor
                        + ",是否在出库位置" + autoTraversingOper.isInPlaceSignal3(),
                this.getClass());

        return false;
    }

    /**
     * 开启起点门
     */
    public boolean openStartDoor() throws IOException {
        if (startFromDevice) {
            if (!autoTraversingOper.checkDoorStatus(taskIn, false)) {
                LoggerUtil.info(autoTraversingOper.getDeviceName() + (taskIn ? "1" : "3") + "方向开门是否降到位"
                        + autoTraversingOper.isAutoDoorDrop1(), this.getClass());
                return false;
            }
            //如果入库，则开启3方向门,出库开启1方向门
            autoTraversingOper.doorRun(!taskIn, true);
        } else {
            if (!autoTraversingOper.checkDoorStatus(!taskIn, false)) {
                LoggerUtil.info(autoTraversingOper.getDeviceName() + (taskIn ? "3" : "1") + "方向开门是否降到位"
                        + autoTraversingOper.isAutoDoorDrop1(), this.getClass());
                return false;
            }
            //如果入库,则开启1方向门,出库开启3方向门
            autoTraversingOper.doorRun(taskIn, true);
        }
        return true;
    }

    /**
     * 等待起点门开启完成
     */
    public boolean waitOpenStartDoor() {
        //起点为基础输送线，如果入库，则等待开启3方向门,出库等待开启1方向门
        //起点为自动横移机，如果入库,则等待开启1方向门,出库等待开启3方向门
        if (startFromDevice == taskIn) {
            if (!autoTraversingOper.isAutoDoorRise3() || autoTraversingOper.isRunning()) {
                LoggerUtil.info("等待" + autoTraversingOper.getDeviceName() + "3方向门开启到位且停止运行", this.getClass());
                return false;
            }
        } else {
            if (!autoTraversingOper.isAutoDoorRise1() || autoTraversingOper.isRunning()) {
                LoggerUtil.info("等待" + autoTraversingOper.getDeviceName() + "1方向门开启到位且停止运行", this.getClass());
                return false;
            }
        }

        return true;
    }

    /**
     * 终点设备进货
     */
    private boolean endDeviceInCargo() throws IOException {
        if (autoTraversingOper.isInPlaceSignal3()) {
            if (startFromDevice) {
                autoTraversingOper.setConveyorForward();
            } else {
                monomerConveyorOper.converyRun(SingleConveryRunType.CONVERY, direction, ConveryTaskType.IN);
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * 起点设备出货
     */
    private boolean startDeviceOutCargo() throws IOException {
        if (autoTraversingOper.isInPlaceSignal3()) {
            if (startFromDevice) {
                monomerConveyorOper.converyRun(SingleConveryRunType.CONVERY, direction, ConveryTaskType.OUT);
            } else {
                autoTraversingOper.setConveyorReverse();
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * 等待进货完成
     */
    private boolean waitInCargoEnd() throws IOException {
        //起点无货，不运行；终点不运行，入库则入库光电有货，出库则出库光电有货
        if (startDevice.hasCargo()) {
            LoggerUtil.info("等待" + startDevice.getDeviceName() + "无货", this.getClass());
            return false;
        }

//        if (startFromDevice && startDevice.isRunning()) {
//            //起点无货，停止运行
//            PlcOperImpl.getPlcOper(monomerConveyorOper.getPlc()).StationRunning(monomerConveyorOper.getDeviceNo(), Direction.STOP);
//        }

        if (startDevice.isRunning()) {
            LoggerUtil.info("等待" + startDevice.getDeviceName() + "停止运行", this.getClass());
            return false;
        }

        if (endDevice.isRunning()) {
            LoggerUtil.info("等待" + endDevice.getDeviceName() + "停止运行", this.getClass());
            return false;
        }

        if (taskIn) {
            if (!endDevice.inCargoSignal()) {
                LoggerUtil.info("等待" + endDevice.getDeviceName() + "入库光电有信号", this.getClass());
                return false;
            }
        } else {
            if (!endDevice.outCargoSignal()) {
                LoggerUtil.info("等待" + endDevice.getDeviceName() + "出库光电有信号", this.getClass());
                return false;
            }
        }

        return true;
    }

    /**
     * 关闭起点门
     */
    public boolean closeStartDoor() throws IOException {
        if (startFromDevice) {
            autoTraversingOper.doorRun(!taskIn, false);
        } else {
            autoTraversingOper.doorRun(taskIn, false);
        }
        return true;
    }

    /**
     * 等待关闭起点门完成
     */
    public boolean waitCloseStartDoor() {
        //起点为基础输送线，如果入库，则等待关闭3方向门,出库等待关闭1方向门
        //起点为自动横移机，如果入库,则等待关闭1方向门,出库等待关闭3方向门
        if (startFromDevice == taskIn) {
            if (!autoTraversingOper.isAutoDoorDrop3() || autoTraversingOper.isRunning()) {
                LoggerUtil.info("等待" + autoTraversingOper.getDeviceName() + "3方向门下降到位且停止运行", this.getClass());
                return false;
            }
        } else {
            if (!autoTraversingOper.isAutoDoorDrop1() || autoTraversingOper.isRunning()) {
                LoggerUtil.info("等待" + autoTraversingOper.getDeviceName() + "1方向门下降到位且停止运行", this.getClass());
                return false;
            }
        }

        return true;
    }

    /**
     * 结束
     */
    private void end() throws IOException {
        endProcess = true;
        LoggerUtil.info(startDevice.getDeviceName() + "->" + endDevice.getDeviceName() + " 输送任务完成",
                this.getClass());
    }

    /**
     * 流程是否结束
     * @return
     */
    public boolean isProcessEnd() {
        return processStatus == AutoTraversProcessStatus.END;
    }
}
